                                 
   
                     Aleph One !PC Application
                   Device Driver Kit Release 2.15
                                 
Version and Copyright information
   This document is version 2.15, dated 22-Oct-1997, by Ian Harvey
   and Wookey (wookey@aleph1.co.uk). This is intended for use with  
   !PC versions 2.15  and later,  although  most of it  applies to 
   versions from 1.995 onwards.

   This document, and any accompanying files, are copyright Aleph 
   One Ltd  1996,1997.  They may  not be,  by any method,  copied, 
   published or distributed to a third party, without the explicit 
   consent of Aleph One Ltd.

Version History: 
 v1.00 Original Issue
 v2.00 HPC mechanism detailed
 v2.02 SYS_FEState changed for v1.00 compatibility
 v2.05 DMA Emulation mechanism clarified
 v2.15 GetinternalAddr SWI changed for callback optimisation
 v2.33 BIOS PC-control Int15, Fn BA had single-task added.
   
   
1. Introduction
   The  Aleph One !PC application, which provides software support
   for Acorn and Aleph One PC emulation boards, is designed in  an
   extensible way which allows software writers to add  a  variety
   of  extra  facilities to the PC environment. The Device  Driver
   Kit  (DDK)  provides documentation and source  code  to  assist
   this. This release of the DDK applies to versions 2.0 and later
   of the !PC application.
   
   In this section, basic terms are defined and an introduction is
   given  to  the  fundamental concepts. It is  assumed  that  the
   reader will have some knowledge of PC architecture
   
   
1.1 Terms and definitions
   The  plug-in emulation hardware manufactured by Acorn or  Aleph
   One is called a PC Card. The 386, 486 (or higher) processor  on
   the  PC card is generally referred to here as the PC. The Acorn
   host  machine (Archimedes, A5000, Risc PC etc) and is generally
   referred  to as the ARM. Occasionally, a distinction  is  drawn
   between different types of PC card: a podule PC card is one  of
   the earlier generation which plugs into a podule expansion slot
   in  all varieties of Acorn machine; a second processor PC  card
   is  one of the range designed to plug into the second processor
   slot on a RISC PC motherboard.
   
   Note  that in the PC world, a word usually refers to  a  16-bit
   integer  and  a longword is a 32-bit one. In the ARM  world,  a
   word  is usually 32 bits. We will endeavour to make word  sizes
   explicit in this document.
   
   
 1.2  !PC emulation basics
   The  PC  world consists of a processor, memory, and  a  set  of
   input/output devices. The processor, memory, and some basic I/O
   devices  (principally the system interrupt controller  and  the
   system timer) are provided by the hardware on the PC card. Some
   podule  PC cards also provide 'real' parallel and serial ports.
   Other I/O devices are provided through emulation in !PC.
   
   Emulation in !PC is done in two principal ways: At a low level,
   it  emulates the I/O ports, interrupts, memory spaces, and  the
   like, used by bits of PC hardware. At a high level, it provides
   a  general mechanism for commands and data to be passed between
   the  PC  and  the ARM. Low-level emulation is used for  devices
   where   the   hardware  is  frequently  accessed  directly   by
   application programs - the VGA screen, the keyboard, the mouse,
   serial  ports,  etc. High-level emulation is used  for  devices
   which are usually only accessed through a well-defined software
   interface;  this  includes hard disk, floppy  disk,  and  CDROM
   drives.  It  may  also be used in cases where there  is  no  PC
   standard  hardware  for the job e.g. systems  for  transferring
   data  between  RISCOS  and  Windows  applications.  It  is,  in
   general,  a much faster way to transfer data than by  low-level
   emulation.
   
   Both low-level and high-level emulations in !PC may be expanded
   by  writing device modules which run on the ARM. For high-level
   emulation, it will be necessary to write drivers to run on  the
   PC to make the services available to DOS or Windows.
   
   
   
   
   
2. Device module components
2.1 Low-level overview
   PC  hardware  devices  use one or more of  the  following:  I/O
   ports,  memory-mapped regions, interrupt request  (IRQ)  lines,
   and  DMA  controller channels. Device modules which  use  these
   provide handler functions to !PC - these are (generally  short)
   subroutines which simulate the effect of a particular action by
   the PC.
   
   A device which provides the PC with I/O ports needs to supply a
   set  of  four  handler routines, referred to as Read8,  Read16,
   Write8,  and Write16, together with a range of I/O  ports  over
   which they apply. The Read8 routine is called when the PC makes
   a  byte  read  (IN instruction) to an I/O port in the  device's
   range. It is passed, as a parameter, the address from which the
   PC  did  the IN, and, as a return value, must supply  the  data
   which  the  PC  would  have read from  that  port.  The  Write8
   function is called when the PC makes a byte write (OUT) to  one
   of the device's I/O ports. It is passed the address of the port
   and  the byte of data which was written. Often, the device will
   use  this  function  to  perform some I/O  action  (e.g.  start
   playing a sound). The Read16 and Write16 functions are similar,
   but are used for 16-bit (word) INs and OUTs.
   
   A  device  needing a memory-mapped region operates in just  the
   same  way  -  it supplies !PC with Read8, Read16,  Write8,  and
   Write16 handlers for that region. These get called when the  PC
   makes a memory access in the given range.
   
   Interrupts  from  the  device need no handler  routines  -  !PC
   simply provides a routine which a device can call when it wants
   to generate an interrupt. (All other interrupt acknowledgement,
   prioritization, etc, is dealt with by the hardware).
   
   A device using a DMA channel must do two things: It should call
   a  routine  supplied  by !PC when it wants  to  request  a  DMA
   transfer (equivalent to asserting a DREQ line on the ISA  bus).
   It  should also supply !PC with a DMA handler function which is
   called  when  the  PC  is  ready to do the  transfer.  The  DMA
   subsytem  is not available on podule PC cards, due to  hardware
   limitations.
   
   
2.2 High-level  (HPC) overview
   The High-level Procedure Call (HPC) mechanism allows the PC  to
   make  a  'call'  to  a procedure on the ARM. PC  side  software
   assembles parameters and data for the call into a packet,  then
   hands this over to the ARM for processing. The ARM performs the
   necessary  processing,  then  assembles  a  packet  of   return
   parameters and data (if appropriate), and passes this  back  to
   the PC.
   
   The  procedure calls which the ARM makes available are  divided
   into  'services' e.g. the hard disk service or the floppy  disk
   service.  Within  each service are functions and  subfunctions,
   specific   to  that  service.  A  device  module  may   provide
   additional  service(s)  by  supplying  a  dispatch  routine  to
   process all the function requests for the service.
   
   If  you  have a choice, you should add functionality to !PC  by
   using HPC. The mechanics of data transfer are hidden by it;  it
   will  use  the  fastest data transfer method available  on  any
   particular PC card hardware.
   
   
2.3 Event and Config handlers
   Any  type  of  device may supply !PC with  a  number  of  event
   handler  routines.  This  allows !PC to  notify  a  device  (by
   calling the event handler) when a variety of system-wide events
   happen  (e.g.  startup,  shutdown,  switching  modes,  etc).  A
   'polling'  event  is  called  at regular  intervals,  to  allow
   devices  to perform some background processing, and a  'config'
   event allows the device to read data from !PC's Config file.
   
   
2.4 Callbacks
   !PC provides the ability to set up callback routines, analogous
   to  RISCOS callbacks. At times, it is unsafe or inconvenient to
   call  a  function provided by !PC. For instance,  an  interrupt
   service  routine cannot call a subroutine which lives in  !PC's
   application space, because it has no way of knowing whether !PC
   is  paged  in  at the time. At these times, the  address  of  a
   'callback' routine can be passed to !PC via a SWI. When !PC  is
   next  paged  in  and  ready to run, it will call  the  callback
   routine, allowing processing to be continued. If calling a  SWI 
   would take too long in  time-critical code, it is  now possible 
   to get the addresses of the relevant routines in the module and 
   call them directly, taking suitable precautions about processor 
   mode changes.
   
   
2.5 Interaction with !PC
   !PC version 2 and later contains two modules which provide SWIs
   of  use to device module writers. The PCSupport module contains
   SWIs   to  generate  interrupts  and  DMA  requests,  and   set
   callbacks.  The PCDevHelp module (new for version  2)  contains
   SWIs which device modules can use to register handler routines.
   
   !PC  announces  that it is starting by issuing a service  call,
   Service_PCDevice. Device modules should install a service  call
   handler when they load, and then wait for this service call. On
   receiving  it,  they  can call various  PCDevHelp  routines  to
   register their handler routines. In previous versions  of  !PC,
   the service call was used to pass the address of two global !PC
   structures, which the device modules had to modify in order  to
   register  routines.  Although this is still  supported,  it  is
   suggested that using PCDevHelp is considerably easier.
   
   
2.6 Service_PCDevice service call details
    Service Call 0x77 - Service_PCDevice
    
    On entry:
    
      R0 = 0
      R1 = 0x77
      R2 = pointer to SYS_State structure (documented in
    SYS.H.HRDSTATE)
      R3 = pointer to SYS_FEState structure (documented in
    SYS.H.FESTATE)
    
    On exit, preserve R0-R3, do not claim this call!
    
   This  is called when !PC has been started and most of the basic
   devices internal to !PC have been registered. The values in  R2
   and  R3  are  addresses  of  two global  structures  which  !PC
   maintains:  SYS_State  contains all the  information  for  I/O,
   memory, event and other types of handlers. SYS_FEState contains
   variables  related to !PC's "front end" state -  full-screen  /
   multitasking  flags, window position, etc. On receipt  of  this
   call,  a  device module should call PCDevHelp SWIs to  register
   its   various  handler  functions,  or  it  can  modify   these
   structures  directly. The SYS_State and SYS_FEState  structures
   are  static  (from  version 2.0 onwards, they  live  in  module
   space),  so it is permitted to modify them at times other  than
   this service call.
   
2.7 PC-side control functions
    Int 15 (system functions), Fn BA allows control of the multitasking 
    state.
    
    On Entry:
     AX=BA00h go to multitask mode
     AX=BA01h quit !PC
     AX=BA02h freeze !PC
     AX=BA03h single-task !PC
     AX=BA04h get current tasking state (NOT IMPLEMENTED)
     
    On Exit:
     AX=00BAh to indicate success. 
     
    The mode change may not happen immediately.
   
3. Support module SWIs
   
3.1 Causing an Interrupt Request
    PCSupport_IrqRequest          &4468E
   
   On  entry:  R0  =  PC  IRQ line on which to generate  interrupt
   request (0-15)
   
   ARM  interrupts will be disabled temporarily. May be called  at
   any  time, except during a FIQ handler. In general, the  actual
   interrupt  will be sent to the PC after this SWI has  returned.
   If  !PC  is  frozen or paged out when this SWI is  called,  the
   request  will be stored until it becomes available  again.  You
   may call this SWI multiple times for one IRQ request.
   
   
3.2 Causing a DMA Request
    PCSupport_DmaRequest          &4468F
   
   On  entry: R0 = DMA channel number on which to generate request
   (0-7)
   
   ARM  interrupts will be disabled temporarily. May be called  at
   any  time,  except  during  a FIQ handler.  It  may  be  called
   multiple times for each request. If !PC is frozen or paged  out
   when  this SWI is called, the request will be stored  until  it
   becomes available again.
   
   
3.3 Setting a !PC callback
    PCSupport_SetCallback         &4468C
   
   On  entry: R0 points to a (static) memory block to be added  to
   the callback list.
   
   [R0+0]      'Magic value'. Set to 0 when the module starts  up;
   do not touch otherwise.
   [R0+4]     Address of function to be called back
   [R0+8]     Value to put in R0 when calling function
   [R0+12]    Value to put in R12 when calling function
   
   This can be called asynchronously (e.g. from an IRQ handler  or
   RISCOS event routine), to schedule a routine to be called  when
   !PC  is  next active. If the value in R12 is zero, the  routine
   will  be  called  back in user mode, with an  application-space
   stack  conforming to APCS rules. If it is non-zero, it will  be
   called   in  supervisor  mode.  The  routine  should   preserve
   registers R4-R11.
   
3.4 Getting addresses of above SWIs to bypass SWI call
    PCSupport_GetInternalAddr     &44691
    
    On Entry: No requirements
    
    On Exit: 
    R0 = Address of IRQRequest SWI fn
    R1 = Address of DMARequest SWI fn
    R2 = Private word of PCsupport module (R12)
    R3 = Address of Set_callback SWI fn
    R4 = Address of Get_callback SWI fn
    
    Note that this call is new in v2.04 of PCsupport, R2 is new  in 
    v2.08, & R3 & R4 are new in v2.15. Do NOT rely on the remaining 
    registers (thru R9) being un-corrupted by this call,  as others 
    may well be added in future.
    
    Do not use  this function lightly. It  is recommended  that you 
    use  the normal  (and much  simpler) SWI  interface unless  you 
    really need the speed!
   
   
3.5 Registering I/O handlers
    PCDevHelp_RegisterIO &4CD01
   
   On entry,
   
   R0 points to a 20-byte Handler structure, as follows
   
   [R0+0]      Address  of  Read8  routine  (see  section  4   for
   description)
   [R0+4]     Address of Read16 routine
   [R0+8]     Address of Write8 routine
   [R0+12]    Address of Write16 routine
   [R0+16]     R12value  -  value to be put in  R12  when  calling
   these routines
   
   R1  is  the  first I/O port, and R2 is the last I/O  port,  for
   which these handlers should be registered.
   
   !PC  only  decodes I/O addresses into 4-byte chunks;  for  this
   reason, R1 should start on a 4-byte boundary, and R2 should  be
   on  a  four-byte boundary plus 3. You cannot therefore register
   for  individual I/O locations (we have not found this to  be  a
   practical  problem). Also, because of the 10-bit  I/O  decoding
   scheme on the ISA bus, R1 and R2 must be in the range 0..3FFh.
   
   The  same  handler  routines  can be  registered  for  multiple
   regions  in I/O space by calling this SWI more than  once.  The
   contents of the Handler structure at [R0] are copied before the
   SWI  returns, so this need only be in temporary storage. It  is
   generally   safe  to  call  this  SWI  at  any  time   -   even
   asynchronously while !PC is running.
   
   
    PCDevHelp_RegisterMem &4CD02
   
   This is in most respects, identical to PCDevHelp_RegisterIO. On
   entry,
   
   R0 points to a Handler structure, as for PCDevHelp_RegisterIO
   
   R1  is  the  first  memory address, and R2 is the  last  memory
   address, for which these handlers should be registered.
   
   !PC  only  decodes  16K memory chunks in the region  A0000h  to
   FFFFFh. Of these, VGA video will use A0000-BFFFFh, and the BIOS
   ROM  occupies  F0000-FFFFFh. Hence R1 will usually  be  one  of
   C0000h,  C4000h,  C8000h ... EC000h, and  R2  will  be  one  of
   C3FFFh,  C7FFFh,  ... EFFFFh. Future !PC developments  may  use
   some of these regions as a shared memory space.
   
   
3.6 Registering DMA handlers
    PCDevHelp_RegisterDMA &4CD03
   
   On entry,
   
   R0 points to a 12-byte DMA_handler structure, as follows
   
   [R0+0]      Address  of  Notify  routine  (see  section  4  for
   description)
   [R0+4]     Address of Transfer routine
   [R0+8]     R12_val - value to be put in R12 when calling  these
   routines
   
   R1  is the DMA channel for which this handler should be called,
   in the rage 0..7. As with the ISA PC architecture, DMA channels
   0..3 are 8-bit channels, 4 is the cascade channel, and 5..7 are
   16-bit channels.
   
   This SWI may be called asynchronously while !PC is running,  as
   for PCDevHelp_RegisterIO and PCDevHelp_RegisterMem.
   
   
   
3.7 Registering Event Handler functions
    PCDevHelp_RegisterEvent &4CD04
   
   On entry, R0 points to a 12-byte CallList structure:
   
   [R0+0]     Address of event handler function
   [R0+4]     list link field - leave this alone
   [R0+8]     R12value - value to put in R12 when calling
   
   R1 is the event number (see section 4 for event descriptions).
   
   This function takes the block which R0 points to and adds it to
   the  list of functions for a given event. Unlike the blocks  in
   previous SWIs, this data is not copied, therefore
   
   -  it  should  be static in module space (either  part  of  the
   module's static data or allocated from the RMA)
   -  for any given block, the SWI should only be called once  for
   each time the Service_PCDevice service call is made.
   -  it  is only safe to call this SWI from the service call,  or
   from another Event Handler routine.
   
   
3.8 Registering Config Handler functions
    PCDevHelp_RegisterConfig &4CD05
   
   On entry, R0 points to a 12-byte CallList structure:
   
   [R0+0]     Address of config handler function
   [R0+4]     list link field - leave this alone
   [R0+8]     R12value - value to put in R12 when calling
   
   The  same  restrictions  apply as for  PCDevHelp_RegisterEvent;
   this SWI is best called once, from the Service_PCDevice service
   call routine.
   
   
3.9 Registering an HPC dispatch function
    PCDevHelp_RegisterHPC &4CD06
   
   On  entry,  R0  points to a 24-byte HPC_handler  structure,  as
   follows:
   
   [R0+0]     List link field - leave this alone
   [R0+4]     HPC service ID number (see HPC description, later)
   [R0+8]     flags field - must be zero
   [R0+12]    request field - must be zero
   [R0+16]     Address of HPC Dispatch function for  this  service
   ID
   [R0+20]    R12_val - value to put in R12 while calling
   
   This  block  will be added to a linked list, as for the  Config
   and  Event  handler functions. Hence, it is best  kept  in  the
   device  module's  static data space, and  this  SWI  should  be
   called once from the Service_PCDevice service call routine.
   
3.10 Getting !PC structure addresses
    PCDevHelp_GetStructAddr &4CD07
   
   This  returns  the addresses of the SYS_State  and  SYS_FEState
   structures.  These structures reside in the RMA; fields  within
   them may therefore be inspected and modified at any time. Note,
   however, that pointers in these structures may refer to objects
   in  !PC's application memory space: you may not use any pointer
   except when !PC is paged in. So, for instance, you may not
   
   - call any function referenced by the structures
   -  insert  anything in any linked list, except at the beginning
   of the list
   - access screen sprite data
   
   except when it is 'safe' to do so: from an event, config,  I/O,
   DMA, memory or HPC handler function, or from a callback routine
   (see the PCSupport_SetCallback SWI).
   
   On exit:
   
   R0 points to the SYS_State structure
   R1 points to the SYS_FEState structure
   Note  that  the addresses returned from this SWI will  only  be
   valid  while the current copy of !PC is running. Once  !PC  has
   quit  (generating  a SYS_Shutdown) event, you  should  not  use
   these  addresses. If !PC is run again, the structures will  all
   be  re-initialised - any modifications you wish to make to them
   should be made again.
   
   
4. Handler function calling conventions
   In general the calling conventions for routines supplied by the
   device  module are based on the APCS standard; while  they  are
   not  directly compatible with relocatable-module C  code,  they
   will  work with 'IRQ handler' wrappers generated with the  CMHG
   utility,  or can be written directly in assembler. !PC  can  be
   given  a  value  to  be placed in R12 before  the  function  is
   called;  if  using  IRQ handler wrappers, this  should  be  the
   module's private word.
   
   
4.1 Memory and I/O handler functions
   A  read  function takes an address (memory or I/O) and  returns
   the data which would have been read from that location. A write
   function  takes  an address and the data written  by  the  CPU;
   there is no return value. Handlers are only called when !PC  is
   paged in and the emulation is running.
   
   These  functions  are  always  called  with  the  processor  in
   supervisor mode, for efficiency reasons. This should present no
   problems  for  module code; note, however, that  space  on  the
   supervisor stack is extremely limited.
   
   
   Read8 function
   
   On entry:
   
   R0          Address  from which PC is making a byte read;  this
       is a port number in the range 0-0xFFFF for I/O handlers, or
       a  physical memory address in the range 0xA0000-0xFFFFF for
       memory handlers.
   
   R12         Value  specified in R12value field of  the  Handler
       structure  for  this  memory  or  I/O  location  (see   the
       SYS.H.STDTYPES for a description of this structure).
   
   R13_svc    Supervisor-mode stack.
   R14_svc    Return address.
   
   
   On exit:
   
   R0          bits  0..7 are read data to be passed back  to  the
       PC. Bits 8..31 are don't care.
   
   R4-R11 should be preserved. Use MOVS  PC, R14 to exit.
   
   
   Read16 function
   
   Register usage as per Read8 function, except:
   
   On entry:
   
   R0          Address  from  which PC is making a  word  (16-bit)
       read.  This  address  is guaranteed  even  (i.e.  aligned).
       Misaligned word reads are treated as two byte reads.
   
   On exit:
   
   R0          bits  0..15 are read data to be passed back to  the
       PC. Bits 16..31 are don't care.
   
   
   Write8 function
   
   Register usage as per Read8 function, except:
   
   On entry:
   
   R0         Address to which PC has written a byte.
   R1          bits  0..7 are data written by PC. bits  8..31  are
       guaranteed clear.
   
   On exit:
   
   R0         don't care
   
   
   Write16 function
   
   Register usage as per Read8 function, except:
   
   On entry:
   
   R0          Address  to which PC has written a word. Guaranteed
       aligned (even).
   R1          bits 0..15 are data written by PC. bits 16..31  are
       guaranteed clear.
   
   On exit:
   
   R0         don't care
   
   
   4.1.1 I/O aliasing note
   In  the ISA PC architecture, although the I/O space is 64Kbytes
   in  size, using 16-bit addresses, only the least significant 10
   bits are decoded. This means that a device which lives at, say,
   I/O  port  100h also has 'aliases' living at 500h,  900h,  D00h
   ..FD00h. !PC does the same - although it passes the full 16-bit
   I/O  address to the handler functions, it only uses  the  least
   significant  10 bits to determine which handler to call.  Thus,
   if  you register some handlers for the region 2E0h to 2E3h, the
   handler  would also get called if the PC did an I/O  access  to
   6E2h. If the device you are emulating does 16-bit I/O decoding,
   you  can  treat theses accesses differently. Otherwise,  ignore
   the highest 6 bits of the address by ANDing it with 3FFh.
   
   
   4.1.2 Compatibility note
   Older  podule  PC cards had a hardware limitation  which  meant
   that  dynamic RAM refresh was suspended while a handler routine
   was  executed.  For this reason, the handler routine  must  not
   take "too long" before returning. In practise, 20ms or so seems
   acceptable,  whereas a second isn't. If your  handler  routines
   take longer than this, the device will work on second-processor
   PC  cards but will crash podule PC cards in unpredictable ways.
   Don't start giving me funny looks, it wasn't my fault.
   
   
4.2 Event handler functions
   There are currently about a dozen different event types in !PC.
   For  each  event,  !PC maintains a list of addresses  of  event
   handler functions. When an event occurs, each function  on  the
   list is called; no parameters or return values are passed. Each
   event is given a number (e.g. SYS_PollChain is number 2).
    
   4.2.1 Event Types
    
    SYS_HardReset                 0
    
   This is called when !PC is being reset, during startup or after
   'reset'  has  been  selected from the menu.  Device  emulations
   should view this event as the equivalent of a power-on reset or
   the  reset  button being pressed. All emulated hardware  should
   clear its state when this event occurs.
    
    SYS_PollChain                 2
   
   This  is  called at regular intervals (typically every 20-30ms)
   while  the  emulation is running; it may be used for background
   processing.  This event is used, for example, by  the  keyboard
   emulation to check if keys have been pressed.
   
    SYS_Shutdown                  3
   
   This  is  called  when  !PC is being quit for  whatever  reason
   (normal  or error exit). After this event is called,  no  other
   events  or  handlers will be called, and it will be  unsafe  to
   request any further services from !PC. If the device module has
   allocated  any  resources as a result of  the  Service_PCDevice
   service  call, or the SYS_SetConfig event, it should free  them
   on this event.
   
    SYS_StartFE                   4
    
   This  is  called when the system is about to start  running  in
   full-screen  mode.  It  is used, for  instance,  by  the  video
   emulation to set the correct RISCOS screen mode.
   
    SYS_StopFE        5
    
   This  is  called  when full-screen mode is about  to  end,  for
   whatever reason (switching back to multitasking mode, an  error
   occuring,  !PC  quitting). A SYS_StartFE event will  always  be
   paired with a SYS_StopFE event - any system resources which are
   allocated or opened on the former should be freed or closed  on
   the latter.
   
    SYS_StartWinFE    6
   
   Occurs  whenever execution in multi-tasking mode  is  about  to
   begin  - on initial startup, or on a switch from single-tasking
   to  multi-tasking mode (in which case, SYS_StartWinFE  will  be
   called after SYS_StopFE).
   
    SYS_StopWinFE                 7
    
   Called  when execution in multi-tasking mode is about to  stop,
   for  whatever reason. This will always be called in a pair with
   SYS_StartWinFE.
   
    SYS_SetConfig                 8
    
   On  system startup, this will be called after all the lines  in
   the  Config file have been processed. This is necessary so that
   devices whose operation depends on a number of config lines can
   know  when to initialise. It is a good point at which to  check
   that  configuration data is complete and correct. Many  devices
   also  use  this  event  to add memory or I/O  handlers  to  the
   SYS_State  tables. Any system resources allocated or opened  on
   this event should be freed or closed on the SYS_Shutdown event;
   these two events will always occur in pairs.
   
    SYS_GainFocus                 9
    
   In  multitasking mode, called whenever the !PC main window  has
   just been given the input focus.
   
    SYS_LoseFocus                 10
    
   In  multitasking  mode, called whenever the  window  loses  the
   input  focus;  this always occurs in a pair with SYS_GainFocus.
   (Focus  is always considered lost when !PC quits for any reason
   - if !PC has focus and an error occurs, LoseFocus will still be
   called before !PC terminates).
   
    SYS_ConnectMouse  11
    
   Called  when  the  user selects 'Connect Mouse'  from  the  !PC
   window  menu.  This event is largely of interest to  the  mouse
   emulation.
   
   
   4.2.2 Event Handler functions
   Event  functions  are registered by adding  a  struct  CallList
   structure  to the appropriate entry in the eventlist  array  in
   the  SYS_State structure. If R12value in the associated  struct
   CallList is zero, then
   
   On entry, processor is in user mode, and
   
   R0-R9         Undefined
   R10, R11, R13      !PC    Application    stack    pointer/stack
               limit/frame pointer as per APCS standard.
   R12           Zero
   R14           Return address as per APCS
   
   On exit:
   
   R0-R3, R12 Can be modified
   R4-R11     Must be preserved
   
   
   If R12value  is  non-zero, the processor will be in  supervisor
       mode, and
   
   R0-R11     Undefined
   R12           R12value
   R14_svc    Return address (use MOVS PC, LR)
   
   On exit:
   
   R0-R3, R12 Can be modified
   R4-R11     Must be preserved
   
   
   
4.3 Config handler functions
   A  device  may  wish to keep configuration information  in  the
   !PC.Config file. To assist this, !PC maintains a linked list of
   Config  handler  functions.  A  device  which  wants  to   read
   information  adds a function to this list (Config_list  in  the
   SYS_State  structure).  Then, as !PC is processing  the  Config
   file, it will pass any line which it does not recognise to each
   function  on the list in turn. A function can 'claim' the  line
   if  it  recognises it, otherwise it will be passed to the  next
   function.  If  the line is not recognised by any function,  !PC
   will display an error message.
   
   If R12value in the associated struct CallList is zero, then
   
   On entry, processor is in user mode, and
   
   R0         Address of string buffer containing a line from  the
               config  file.  This string is null-terminated,  but
               may  have  CR  and/or LF at the end, or  end  in  a
               comment (with a ''#" character).
   
   R1-R9         Undefined
   R10, R11, R13      !PC    Application    stack    pointer/stack
               limit/frame pointer as per APCS standard.
   R12           Zero
   R14           Return address as per APCS
   
   On exit:
   
   R0             1  if  line  has been 'claimed'. 0 if  line  not
       recognised by device.
   R1-R3, R12 Can be modified
   R4-R11     Must be preserved
   
   
   If R12value  is  non-zero, the processor will be in  supervisor
       mode, and
   
   R0         Address of string buffer, as before
   R1-R11     Undefined
   R12           R12value
   R14_svc    Return address (use MOVS PC, R14)
   
   On exit:
   
   R0            1 if line processed, 0 if not recognised
   R1-R3, R12 Can be modified
   R4-R11     Must be preserved
   
   
4.4 DMA handler functions
   A  device which attaches to a DMA channel needs to provide  two
   routines,  a  'notify' function and a 'transfer' function.  The
   'Notify'  function  is called when the DMA controller  is  made
   ready  to perform a transfer; it is intended to assist  devices
   which  have  to  do some preparation in advance of  the  actual
   transfer, and to let a device keep track of what is going on in 
   the DMA controller.  ( 'Notify' is called  when the channel  is 
   unmasked,  or when autoinitialise occurs if the channel  is  so
   programmed).  The  'Transfer' function is  called  when  a  DMA
   transfer  is  taking  place; this may be  because  the  PC  has
   started the DMA controller in software, or because a device has
   called  the  DMA_Request  function or the  PCSupport_DmaRequest
   SWI.  If calling  a SWI  would take  too long  in time-critical 
   code, it is  now possible to get the  addresses of the relevant 
   routines in the module and call them directly,  taking suitable  
   precautions about processor mode changes.
   
   
   4.4.1 DMA Handler Notify function
   The Notify is passed the following parameters:
   
   mode  indicates the type of transfer: it is one or more of  the
   following groups of values ORed together:
   
     DMAMODE_NONE (= 0)'Verify' transfer - is this ever used??
     DMAMODE_WR (= 1)  Memory is being written
     DMAMODE_RD (= 2)  Memory is being read
   
     DMAMODE_REV (= 8) 'Reverse' transfer (address decrements!)
   
     DMAMODE_DEMAND (= 0x00)Demand mode
     DMAMODE_SINGLE (= 0x10)Single mode
     DMAMODE_BLOCK (= 0x20) Block mode
   
   The demand/single/block mode specifiers don't affect the way in
   which  the transfer between !PC and the device module  happens;
   they may simply be useful information for the device module.
   
   PCaddr is the physical PC address of the first location  to  be
   affected  by  the  transfer; this  will  also  be  the  highest
   location if the transfer is 'reversed'.
   
   len is the maximum total length of the transfer, in bytes.
   
   On  entry  to  the  function,  if  R12_val  in  the  associated
   DMA_handler  structure is zero, the processor is in  user  mode
   and:
   
   R0         mode parameter
   R1         PCaddr parameter
   R2         len parameter.
   R3-R9         Undefined
   R10, R11, R13    !PC's  stack pointer/stack limit/frame pointer
               as per APCS standard.
   R12           Zero
   R14           Return address as per APCS
   
   On  exit,  R0-R3  and R12 may be altered, and  R4-R11  must  be
   preserved.
   
   If  R12_val  is non-zero, the processor is in supervisor  mode,
   and
   
   R0         mode parameter
   R1         PCaddr parameter
   R2         len parameter.
   R3-R11     Undefined
   R12        R12_val
   R13        Supervisor stack pointer
   R14           Return address (use MOVS PC, R14)
   
   On  exit,  R0-R3  and R12 may be altered, and  R4-R11  must  be
   preserved.
   
   
   4.4.2 DMA Handler Transfer function
   The  Transfer  function may be called a number of times  before
   the  whole  transfer is considered completed. It is passed  the
   following parameters:
   
   mode  is  the  DMA  transfer mode, the same as  passed  to  the
   'Notify' function.
   
   PCaddr is the PC physical address of the first location  to  be
   affected, as for 'Notify'.
   
   len is the length of this transfer, in bytes.
   
   ARMaddr  is  the address in ARM logical space corresponding  to
   PCaddr,  the  first byte of the transfer. Memory in  the  range
   ARMaddr..ARMaddr+len-1  (for a normal  transfer)  and  ARMaddr-
   len+1..ARMaddr  (for a reverse transfer) is  guaranteed  to  be
   valid  for the device to use. Note that this only true for  the
   duration  of  the  current handler call:  the  device  may  not
   remember  the  address for use later. If this causes  problems,
   the  device should maintain its own data buffer, and copy  data
   to/from it on this call.
   
   The value returned from the function is made up as follows:
   
   bits 0..30 are the number of bytes actually transferred by  the
   device; it must be in the range 0..len.
   
   bit 31, if set, is used by the device to indicate a 'terminate'
   condition.
   
   
   On  entry  to  the  function,  if  R12_val  in  the  associated
   DMA_handler  structure is zero, the processor is in  user  mode
   and:
   
   R0         mode parameter
   R1         PCaddr parameter
   R2         len parameter.
   R3            ARMaddr parameter
   R4-R9         Undefined
   R10, R11, R13    !PC's  stack pointer/stack limit/frame pointer
               as per APCS standard.
   R12           Zero
   R14           Return address as per APCS
   
   On exit,
   
   R0          Return  value  (bits  0..30  =  length,  bit  31  =
   'terminate' flag)
   R1-R3, R12 Can be altered
   R4-R11     Must be preserved.
   
   If  R12_val  is non-zero, the processor is in supervisor  mode,
   and
   
   R0         mode parameter
   R1         PCaddr parameter
   R2         len parameter.
   R3            ARMaddr parameter
   R4-R11     Undefined
   R12        R12_val
   R13        Supervisor stack pointer
   R14           Return address (use MOVS PC, R14)
   
   On exit,
   
   R0          Return  value  (bits  0..30  =  length,  bit  31  =
   'terminate' flag)
   R1-R3, R12 Can be altered
   R4-R11     Must be preserved.
   
   
   4.4.3 DMA Transfer sequence
   The  Transfer  function may be called more than once  before  a
   given DMA transfer, as programmed by the PC, is completed. Only 
   one Notify call will be issued for each such transfer (although 
   note that at the end of an autoinit transfer an 'extra'  notify 
   will occur at the end as the transfer is reset, but the hardware 
   has not yet  been stopped.  The sequence of  Transfer calls  is
   controlled  partly  by  the DMA  emulation  and  partly by  the 
   device, through the value returned from the  Transfer function. 
   It behaves as follows:
   
   i) The DMA emulation may choose to break a long transfer into a
   series of shorter transfers (e.g. because the transfer area  is
   fragmented in ARM logical space), in an arbitrary fashion.  The
   'Transfer'  function is called once for each shorter  transfer,
   with  the correct address and length parameters. If the  device
   wants  to  know when the transfer is over, it should  save  the
   length passed into the 'Notify' function as a 'Number of  bytes
   left'  value,  and  decrease  it appropriately  each  time  the
   'Transfer' function is called.
   
   ii)  The  device  may break a long transfer into  a  series  of
   shorter  transfers  (e.g.  because the  flow  of  data  from  a
   hardware  device  has dried up). It does this  by  returning  a
   length value which is less than len passed in. If this happens,
   the  DMA  transfer will be temporarily paused. When the  device
   has  more data, it should call the appropriate DMA Request SWI,
   and  the  sequence  will restart when the PC  acknowledges  the
   request. Until it does so, the PC emulation is allowed to  run,
   with the DMA controller in a 'transfer in progress' state.
   
   If  the device wishes to pause a DMA transfer, it should return
   a  value  of 0 from the 'Transfer' function, and take no  other
   action.  The DMA system will then simply wait until DMA_Request
   is called again. This is equivalent to deasserting DREQ in a real 
   DMA transfer. TC will not happen, but the CPU will be allowed to 
   run again.
   
   iii) The DMA emulation will consider the transfer complete (set
   the  'End-of-Process'  flag on the channel,  mask  off  further
   requests or autoinitialise), when either:
   
   -  the  total  number  of  bytes (as  passed  to  the  'Notify'
   function) have been successfully transferred, or
   
   -  the Transfer function has returned with the 'Terminate'  bit
   set.
   
   So the transfer sequence should work like this:

    1) PC (or Autoinit) programs up DMA transfer in controller

    2) When controller is enabled and channel unmasked Notify
       call sent to device claiming that channel.

  - 3) PC software may now start transfer immediately
  |
  | 4) Wait for DMA_Request from device to transfer block. <--
  |                                                           |
  ->5) Transfer function called                               |
       - Device returns amount transferred                    | 
                                                              |
    6) All data transferred?   -------No----------------------
               | Yes
    7) Autoinit bit set? ---Yes---> Go back to step 1
               | No
    8) Transfer complete, set TC bit, clear request bits
    
    Note that at the end of an auto-init transfer sequence the Autoinit bit
    will still be set and the controller may re-init and send a notify before
    being turned off by the driver. This can be ignored if the driver has
    told you to stop.

4.5 HPC Dispatch functions
   This  will  be  called when the PC makes an HPC call  with  the
   service ID for which this function was registered.
   
   On entry, if R12_val in the associated HPC_handler structure is
   zero, processor is in user mode, and
   
   R0         Address  of  buffer containing the HPC data  packet.
               Return  values are also placed in this buffer.  See
               section 5 for more details.
   
   R1-R9         Undefined
   R10, R11, R13      !PC    Application    stack    pointer/stack
               limit/frame pointer as per APCS standard.
   R12           Zero
   R14           Return address as per APCS
   
   If  R12_val  is  non-zero, then the processor is in  supervisor
   mode, and
   
   R0         Address of HPC buffer, as above
   R1-R11     Undefined
   R12           R12_val
   R13        Supervisor stack
   R14_svc    Return address
   
   
   On exit:
   
   R0-R3, R12 Can be modified.
   R4-R11     Must be preserved
   
   
   
5. HPC services
   
5.1 HPC data packets
   When  the PC makes an HPC call to the ARM, it assembles a  data
   packet containing the following information:
   
   - the ARM service to which the call is being directed
   - a function code (reason code) within that ARM service
   - parameters and data for the call.
   
   The parameters for the call must contain enough information  to
   allow the ARM service to work out the length of the packet,  if
   this  is  variable.  (For instance, a disk 'write'  call  would
   contain the number of sectors of data contained in the packet).
   There  is no explicit length field passed to the ARM, only  the
   packet data.
   
   !PC limits the size of the data packet (in either direction) to
   16K bytes; this is not likely to change in a hurry.
   
   The  format  of the data packet passed from PC  to  ARM  is  as
   follows:
   
   byte 0: HPC service ID, bits 0..7
   byte 1: HPC service ID, bits 8..15
   bytes 2..N (N < 16384): depends on service ID
   
   The  HPC  service  ID  is a 16-bit number uniquely  identifying
   service on the ARM side. Numbers already allocated for !PC  are
   as follows:
   
   0000h - System information
   0001h - Hard disk services
   0002h - Floppy disk services
   0003h - CDROM services
   FFFFh - reserved
   
   The following have been allocated to third parties
   
   000Fh - software development
   0105h - Alexander Thoukydides (ARMEdit)
   0106h - Chris Claydon, Armed Forces software (WinRisc)
   0107h - Gary Partis, ASPI drivers (with Powertec card/PowerROM)
   0108h - Andreas Walter, ASPI drivers (Freeware version)

   The  'software  development'  ID  can  be  used  while  testing
   sofware,  before a real ID is allocated. Developers  can  write
   and  test  software using this ID, but should not  release  the
   code  publicly.  Aleph One will allocate  HPC  service  IDs  on
   request.
   
   The data passed back from the ARM to PC at the end of a call is
   also  a  single block, up to 16384 bytes long. If there  is  no
   data  to  return,  this  may be omitted  altogether,  but  most
   services  will want to return some form of success  or  failure
   error code. As a basic minimum, one function within the service
   should  return information, so that the PC software  can  check
   for its presence before using it.
   
   The  format  of this data packet is entirely dependent  on  the
   service and function number. However, when a call is made to an
   unknown  service  ID, the HPC software will return  the  4-byte
   packet:
   
    0xFF 0xFF 0xFF 0xFF
   
   It  is recommended that, if you are designing your own service,
   you  use  the first few bytes of the returned data as an  error
   code;  0  would indicate success, non-zero an error.  An  error
   code of 0xFFFF or 0xFFFFFFFF would indicate that the service is
   unknown.
   
   
5.2 HPC Dispatch function
   A  device module providing HPC service has a main routine which
   dispatches function calls from the PC. In C, this function will
   have the following definition:
   
    void HPC_Dispatch ( BYTE *data_in_out );
   
   This simply takes one parameter, a pointer to a buffer for data
   transfer  between  PC and ARM.  This buffer  will  be  16Kbytes
   long,  and will start on a word-aligned boundary. On  entry  to
   the  call, the buffer will contain the data packet from the  PC
   (i.e.  the  first two bytes will be the service ID,  subsequent
   bytes are service-dependent).
   
   While the call is in progress, the whole 16K buffer may be used
   as  workspace. When the dispatch routine returns, all  returned
   data  should have been placed in the buffer. There are no other
   parameters or returned values: everything goes via the buffer.
   
   The  dispatch  routine is registered with  !PC  by  creating  a
   HPC_handler    structure    for    it    and    calling     the
   PCDevHelp_RegisterHPC SWI.
   
   
5.3 Making HPC calls from the PC under DOS
   The BIOS supplied with !PC provides a service routine which DOS
   and  Windows programs can use to make HPC calls. It is accessed
   via an INT 4Dh instruction.
   
    HPC_Serv_Int equ 04Dh
   
   The various services available are described below:
   
   5.3.1 Function 0000h - identify HPC services
   On entry:
     AX = 0000h
   
   On exit:
     AX = 4850h ('HP') indicates HPC services present
   
   
   5.3.2 Function 0001h - execute HPC call
   On entry:
     AX = 0001h
     ES:BX -> parameter block
   
   On exit:
     AX = 0000h: success
     AX = 0001h: failure - interface to ARM failed
     AX = 0002h: failure - bad parameter values
     AX = 0003h: interface busy, try again.
   
     all other regs preserved
   
   If  the  result is 'interface busy', you should make  the  call
   again (this is only likely to occur under Windows).
   
   The  parameter block at ES:BX is a 26-byte structure containing
   addresses and lengths for the data to be sent and returned from
   the  ARM. For convenience, both the data sent out and the  data
   returned  can  be  split into two portions.  This  allows,  for
   instance,  the parameters for a hard disk write to be separated
   from the data to be written. The structure is as follows:
   
   Offset from ES:BX
   +0..+1     buffer flag: 0000h = buffer A, 0001h = buffer B
   +2..+3     data sent, 1st portion, length
   +4..+7       data  sent,  1st  portion,  address   (real   mode
   segment/offset form)
   +8..+9     data sent, 2nd portion, length
   +10..+13   data sent, 2nd portion, address
   +14..+15   data returned, 1st portion, length
   +16..+19   data returned, 1st portion, address
   +20..+21   data returned, 2nd portion, length
   +22..+25   data returned, 2nd portion, address
   
   The 'buffer flag' is discussed below. It is recommended you use
   buffer  B,  unless  you  are writing  a  low-level  driver  for
   something which may be used as a swap device (e.g. a disk),  in
   which case use buffer A.
   
   Any  'length'  field  above may be  zero,  in  which  case  the
   associated  address will be ignored. Note that the total  'data
   sent' length will have to be at least 2 bytes long in order  to
   contain a valid HPC service ID.
   
   5.3.3 Unknown functions
   Any other value in AX on entry to INT 4Dh will return FFFFh  in
   AX, all other registers unchanged.
   
   
   An  example program, HPCCALL.C, provided with the DDK shows how
   to make an HPC call in Turbo C for DOS.
   
5.4 HPC Buffers and multitasking operating systems
   As hinted above, there is a choice of two 'buffers' to use when
   making an HPC call. These two buffers are notionally identical,
   but  the  low-level mechanism by which the data is  transferred
   makes  it  possible  to be assembling a packet  in  one  buffer
   whilst an HPC call is made using the other one. This simplifies
   affairs   when  writing  drivers  for  multitasking   operating
   systems.
   
   Problems arise in such operating systems due to the fact that a
   number  of  different  drivers may want  to  use  HPC  services
   simultaneously. Suppose that a "user-mode" driver, operating in
   a  virtual  machine, wants to send some data to the ARM.  Also,
   suppose that some of this data has been paged out to disk.  The
   driver  will begin sending the parameters and data out  to  the
   ARM  when it hits a page fault; it will then be interrupted  by
   the  disk driver which will also want to send and receive  data
   from the ARM.
   
   If  there is just one channel through which data can be  passed
   to the ARM, the situation described above creates a problem. If
   the  disk  driver is allowed to proceed, it will overwrite  the
   call from the user-mode driver; if it is stalled (in the belief
   that  it can have a go when the user-mode driver has finished),
   the system will hang.
   
   One possible solution to this is to forbid the user-mode driver
   from  talking directly to the hardware; it should assemble  its
   data  into an area of 'real' (non-pageable) RAM, which is  then
   copied to the ARM in one go. This is probably the normal way of
   solving  such  conflicts,  but  (under  Windows  at  least)  it
   requires  the  writing of an extra virtual  device  driver,  it
   consumes  physical memory and introduces an extra data  copying
   operation.
   
   Instead,  !PC  provides two buffers (A and  B),  in  which  two
   simultaneous  operations  can  be  in  progress.  Buffer  A  is
   designated  (by  PC  side software) as a 'kernel-mode'  buffer.
   This  is for use by the swap device (i.e. the hard-disk driver)
   and  any  other driver which can't be pre-empted  by  the  swap
   device.  Because the kernel ensures that requests to  the  swap
   device  can be properly serialised, and won't be pre-empted  by
   other  requests to the swap device (it wouldn't be possible  to
   write a real hard-disk driver if not!), we can rely on buffer A
   to  always be free when we need it.
   
   Buffer  B  is  a 'user-mode' buffer. This should  be  used  for
   everything else - in particular, anything which may be run in a
   virtual  machine (under Windows 3.x, this means DOS  boxes  and
   Windows   applications).  It  is  thus  shared   between   many
   processes.  The system maintains a lock for buffer  B;  if  one
   process  is  using it when another one wants  to  use  it,  the
   second  process has to wait for the first to finish.  With  the
   swapper  using buffer A, the first process will always be  able
   to finish, even if it causes paging.
   
                            Appendices
                                 
Appendix A - Low-level PC interface to HPC
   This  information  may  prove useful to  those  developing  HPC
   drivers for other operating systems.
   
A.1 I/O Port description
   The HPC interface is provided using the following I/O ports:
   
    HPC_Cmd_Port    equ 0300h   ; Byte only, write only
    HPC_Status_Port equ 0300h   ; Byte only, read only
    HPC_DataA_Port  equ 0304h   ; Byte or word read/write
    HPC_DataB_Port  equ 0306h   ; Byte or word read/write
   
    
    
    HPC_IRQ_line    equ 15
    
   In addition, where the PC card hardware provides shared memory,
   an  32Kbyte area of RAM somewhere in the address range 0xC0000-
   0xEFFFF is used as a data transport area.
   
   
   The  following values may be written to HPC_Cmd_Port (given  in
   binary)
   
    0000 000b ; HPC_WRITE_IO
    0001 000b ; HPC_READ_IO
    0010 000b ; HPC_WRITE_MEM
    0011 000b ; HPC_READ_MEM
    
    010i 000b ; HPC_DO_CALL
    
    0110 000b ; HPC_RELEASE_BUFFER
    
    1000 0000 ; HPC_RESET_ACK
    1000 0010 ; HPC_SUSPEND_ACK
    1000 0100 ; HPC_MULTI_REQ
    1000 0110 ; HPC_QUIT_REQ
    1000 1000 ; HPC_FREEZE_REQ
    
   Bits in the above values are as follows:
   
    b = 0 for data buffer A
    b = 1 for data buffer B
    i = 0 for no interrupt on call completion
    i = 1 for interrupt on call completion
   
   
   
   The value read from HPC_Status_Port is made up as follows:
   
    bit 7: 1 = Suspend requested, 0 = normal operation
    
    bit 5: 1 = ready for command/cmd complete, 0 = busy
    
    bit 1: 1 = buffer B is in use
    bit 0: 1 = buffer A is in use
   
   All  other  bits are reserved and should be ignored. This  port
   may  be read as many times as necessary, without affecting  any
   state.
   
   
A.2 HPC Call Sequence
   To  make  an  HPC  call to the ARM, the PC must decide  whether
   buffer A or buffer B is to be used - see section 5.4 above  for
   a description of this. The sequence is as follows:
   
   1)  Read HPC_Status_Port to determine if the buffer is free for
   use  (bit  0 clear for buffer A, bit 1 for buffer B).  If  not,
   either  the  HPC  code has been re-entered at  an  inconvenient
   time,  or some previous call failed to release the buffer after
   use. In a multitasking environment, the driver should sleep  or
   wait until the buffer is free.
   
   
   2)  If  shared  memory  is  available, write  HPC_WRITE_MEM  to
   HPC_Cmd_Port  and  assemble the 'data  sent'  packet  into  the
   shared memory buffer at the appropriate address (see below).
   
   3)  If  shared  memory isn't available (or is not being  used),
   write  HPC_WRITE_IO to HPC_Cmd_Port and send the data  out  via
   HPC_DataA_Port or HPC_DataB_Port. If you have a lot of data  to
   move,  a 16-bit block out instruction (REP OUTSW) will  be  the
   most efficient way of doing this.
   
   4) As soon as HPC_WRITE_MEM or HPC_WRITE_IO has been written to
   the  command port, the ARM will set the buffer-in-use  bits  in
   HPC_Status_Port to indicate that the buffer is being used. This
   will  lock  the  buffer, preventing this piece  of  code  being
   reentered in a multitasking environment.
   
   5)  When the data packet has been written, send HPC_DO_CALL  to
   HPC_Cmd_Port. This will start the ARM executing the command. If
   it  is  useful, set the 'i' bit in the command byte to get  the
   ARM  to  generate an interrupt when the command  completes.  On
   present  systems, the ARM processor is unlikely to be  able  to
   perform any PC emulation duties while a command is in progress.
   The  amount  of  useful work that the PC can do while  waiting,
   therefore, may be limited, and a polled approach may be just as
   successful.
   
   6)  If an interrupt has been requested, wait for the interrupt.
   Read  HPC_Status_Port; if the 'command-complete' bit isn't set,
   go back to step 6.
   
   7)  If the call returns data and you are using shared memory to
   retrieve them, write HPC_READ_MEM to HPC_Cmd_Port and read back
   the  data  from  the shared memory area. If you are  using  I/O
   ports to retrieve data, write HPC_READ_IO to HPC_Cmd_Port, then
   read the data back from HPC_DataA_Port or HPC_DataB_Port. A REP
   INSW  instruction  will be the most efficient  means  of  doing
   this.
   
   8)  Before finishing, write HPC_RELEASE_BUFFER to HPC_Cmd_Port.
   If  (for  any reason) your code has to abort early  due  to  an
   error,  you should still write this value if any other  command
   has been written to the command port (i.e. you have got to step
   2 or later).
   
   Note:  All  the values written to HPC_Cmd_Port in the  sequence
   above  will vary depending on whether buffer A or buffer  B  is
   being used.
   
   
A.3 Suspend mode
   On older, podule PC card hardware it is necessary to put the PC
   system  into  a 'suspend' mode while the ARM is processing  any
   time-consuming  operation, in order to prevent refresh  to  the
   DRAM  failing.  While in the suspend mode, the  PC  should  not
   attempt  to  access any memory or I/O ports which  require  ARM
   emulation (i.e. just about all of them), and should simply wait
   until told (via an interrupt) to continue.
   
   Suspend  mode  also  provides a means  of  reducing  the  power
   consumption of the PC card when it is idle.
   
   When  the  ARM requires suspend mode, it will set the  'Suspend
   Requested' bit in HPC_Status_Port, and deliver an interrupt (on
   HPC_IRQ_line). The PC should, at its earliest convenience:
   
   1) Disable all interrupts except HPC_IRQ_line
   2) Write the value HPC_SUSPEND_ACK to HPC_Cmd_Port
   3)  Enter  a  loop (or execute a HLT instruction,  for  maximum
   power-saving benefits) which waits for an interrupt to occur.
   4)  When  an  interrupt  occurs, read HPC_Status_Port.  If  the
   'Suspend Requested' bit is still active, return to step 3
   5) Re-enable other interrupts, and continue as before.
   
   Note that the interrupt on HPC_IRQ_line may coincide with other
   reasons  for  an interrupt (such as HPC command completion,  or
   'request'  state  becoming active).  In  this  case,  only  one
   interrupt will be generated.
   
   As  an  example,  the following pseudo-C illustrates  how  this
   might be done.
   
    int HPCstatus;  /* Global copy of HPC_Status_Port */
    
    
    void HPC_ISR ( void )  /* Interrupt service routine for
    HPC_IRQ_line */
    {
      Acknowledge_Int_To_8259s;
      Reenable_Ints;          /* Allow other ints to happen */
    
    irq_check:
      HPCstatus = ReadPort ( HPC_Status_Port );
    
      if ( HPCstatus & 0x80 ) /* Suspend bit set */
      {
        Disable_all_IRQs_except_HPC_IRQ_line;
        /* Enter suspend mode, if appropriate */
        WritePort ( HPC_Cmd_Port, HPC_SUSPEND_ACK );
    
        while( HPCstatus & 0x80 )  /* We will get re-entered here!
    */
          ;                        /* 2nd interrupt will reset
    HPC_Status */
    
        Reenable_other_IRQs;
      }
    
      /* Process other bits */
    
      if ( HPCstatus & 0x20 )
      {
        Do_HPC_Command_Complete_processing;
        goto irq_check;
      }
    
    }
    
   
A.3 Shared memory availability
   Some  (but not all) PC Card implementations make the  HPC  data
   buffers  available  directly in PC memory space,  to  save  the
   overhead  of using port I/O to transfer the data.  If  so,  the
   starting addresses of the two 16K buffers can be found by using
   the  HPC_SYS_INFO command, below. It is recommended  that,  for
   any  one HPC call, either shared memory or port I/O is used  to
   transfer the data, but not a mixture.
   
   
Appendix B - Standard !PC HPC calls
   !PC  provides a number of standard services which  drivers  may
   use  for  accessing !PC's hard disks, floppy disks, and CDROMs.
   The  data  format  for  each  command  is  given  in  following
   sections.  All  standard !PC commands begin with 2-bytes  (LSB,
   MSB)  giving the service ID (as normal), followed by  a  2-byte
   reason code (LSB, MSB). In the description of each command, the
   notation 'serviceID:reasoncode' is used to describe this.
   
   16-bit  quantities  take  up 2 bytes,  are  aligned  to  2-byte
   boundaries, and are stored LSB-first.
   32-bit  quantities  take  up 4 bytes,  are  aligned  to  4-byte
   boundaries, and are stored LSB-first.
   
   
B.1 System Information service (service ID = 0000h)
   Currently, only one call is defined:
   
   B.1.1 HPC_SYS_INFO (0000h:0000h)
   Data Sent:
   
     00h 00h 00h 00h
   Service ID/reason code
   
   Data Returned:
   
    48h 50h 43h EEh
   4-byte HPC identifier.
   
    <version_minor> <version_major>
   2  bytes. For this spec, the major version number is 1, and the
   minor  version  number  is  0. Revisions  which  are  upwardly-
   compatible  with  this  spec  will only  change  minor  version
   number.  A  major version number change means a  non-backwards-
   compatible change.
   
    00h 00h
   Two pad bytes
   
    <buffer_A_addr>
   4 bytes. If shared memory is available, this will be the 32-bit
   physical  address of the 16Kbyte data buffer A.  This  will  be
   aligned  to a 16K boundary, and will usually be placed  in  the
   first  1Mb of physical address space. If shared memory  is  not
   available, this value will be 0FFFFFFFFh (-1). PC side  drivers
   must be able to cope with this and revert to using I/O ports to
   transfer data.
   
    <buffer_B_addr>
   4  bytes.  Starting address of data buffer B, or -1. All  other
   details are as above.
   
   
   
B.2 Hard disk services (service ID = 0001h)
   B.2.1 HPC_HD_INFO (0001h:0000h)
   Returns information on hard drives.
   
   Data Sent:
   
    01h 00h 00h 00h
   Service ID/reason code
   
    <drvnum>
   2   bytes.  16-bit  hard  drive  number.  Drives  are  numbered
   consecutively starting at 0000h.
   
   
   Data Returned:
   
    <status>
   2 byte error code: see error summary below.
   
    <NDrives>
   2  bytes.  Number  of  hard disk drives  present.  Valid  drive
   numbers  are  in the range 0 to NDrives-1. If the  given  drive
   number  was  not  in  this  range,  all  the  following   drive
   parameters  will  be  zero (although  no  error  is  returned).
   Additionally, some versions of !PC allow local IDE drives to be
   used  for drive numbers 0 or 1; if so, these drive numbers will
   also return all zeros.
   
    <drvsize>
   4  bytes. Drive capacity, expressed as a 32-bit number of  512-
   byte  sectors.  (For simplicity, all hard disks are  given  the
   appearance  of having 512-byte sectors). Sectors on  the  drive
   are numbered consecutively in the range 0 to drvsize-1.
   
    <flags>
   4 bytes. Contains a 32-bit flags word, as follows:
   
   bit 0: 1 if drive is read-only
   bit 1: 1 if drive is removeable (i.e. contents may change)
   bit  2:  1  if drive is lockable (meaningful only if  drive  is
   removeable)
   bit 3: 1 if drive is volatile (e.g. a RAM disk)
   
   Note that current !PC doesn't support any of these options.
   
    <Ncyls>
    <Nheads>
    <Nsects>
   2 bytes each. For those operating systems (e.g. DOS) which need
   cylinders,  heads, and sector numbers in order to  boot,  these
   are  the  suggested values. (Where these values come from  will
   depend  on the precise nature of the drive). Ncyls will  be  in
   the  range  1..1024, Nheads in the range 1..255, and Nsects  in
   the range 1..63.
   
   
   B.2.2 HPC_HD_READ (0001h:0001h)
   Reads data from the disk.
   
   Data Sent:
   
    01h 00h 01h 00h
   Service ID/reason code
   
    <drvnum>
   2 bytes. 16-bit hard drive number.
   
    <sect_count>
   2  bytes. Number of sectors to read. (With a 16K data buffer, a
   maximum of 31 sectors may be read in one go).
   
    <sect_start>
   4 bytes. 32-bit sector number where reading is to start.
   
    <readahead>
   2  bytes. If this read is part of a longer data transfer  being
   broken down into blocks, this number should be set to the total
   number  of  sectors still to be read (including this transfer).
   If !PC is capable of providing read-ahead support, setting this
   value  should enhance performance. readahead may safely be  set
   to zero if the total transfer size is unknown.
   
   
   Data Returned:
   
    <status>
   2  byte error code: see error summary below. Following data  is
   valid only if this status is 0000h (success).
   
    00h 00h
   2 pad bytes.
   
    <read data>
   If  the read was successful, 512*sect_count bytes of data  from
   the hard disk.
   
   
   B.2.3 HPC_HD_WRITE (0001h:0002h)
   Writes data to the disk.
   
   Data Sent:
   
    01h 00h 02h 00h
   Service ID/reason code
   
    <drvnum>
   2 bytes. 16-bit hard drive number.
   
    <sect_count>
   2 bytes. Number of sectors to write. (With a 16K data buffer, a
   maximum of 31 sectors may be written in one go).
   
    <sect_start>
   4 bytes. 32-bit sector number where writing is to start.
   
    <writeahead>
   2  bytes. If this write is part of a longer data transfer being
   broken down into blocks, this number should be set to the total
   number   of  sectors  still  to  be  written  (including   this
   transfer).  If  !PC  is  capable of providing  write  buffering
   support,   setting   this  value  will   enhance   performance.
   writeahead may safely be set to zero if the total transfer size
   is unknown.
   
    00h 00h
   Pad bytes.
   
    <write data>
   512*sect_count bytes of data to be written to disk.
   
   Data Returned:
   
    <status>
   2 byte error code: see error summary below.
   
   
   B.2.4 HPC_HD_MEDIA (0001h:0003h)
   Performs various functions related to removable-media drives.
   
   Data Sent:
   
    01h 00h 01h 00h
   Service ID/reason code
   
    <drvnum>
   2 bytes. 16-bit hard drive number.
   
    <fncode>
   2 bytes. Subfunction number, as follows:
   
   0000h:  Check  whether media has changed.  If  it  has,  return
   'Media  Changed'  error (see error summary  below).  Note  that
   whenever the media has been changed, this command and the  read
   &  write  commands  will  perform no action  and  return  Media
   Changed until it is cleared with the 'clear' or 'lock' commands
   below.
   
   0001h: Clear media-changed status. This must be done whenever a
   'Media Changed' condition has occurred.
   
   0002h:  Lock  drive/clear  changed  status.  If  the  drive  is
   lockable,  this locks the media in the drive (and  also  clears
   the  media changed flag). While the drive is locked, no  media-
   changed errors will be returned.
   
   0003h: Unlock drive.
   
   Function  codes 0..1 are available if the drive is  flagged  as
   being  removeable-media, and codes 2..3 are available if it  is
   flagged as being lockable.
   
   Data Returned:
   
    <status>
   2 byte error code: see error summary below.
   
   
   B.2.5 Error codes
   Error codes are as follows:
   
   0000h - success
   0001h - unknown reason code
   0002h  -  bad parameters (drive no, sector count or subfunction
   number)
   0003h - bad sector number
   0004h - data error during read or write
   0005h - write protect violation
   0006h - media has been changed (on removeable drives only)
   
   FFFFh - HD HPC services not installed
   
   
B.3 Floppy disk services (service ID = 0002h)
   B.3.1 HPC_FD_INFO (0002h:0000h)
   Data Sent:
   
    02h 00h 00h 00h
   Service ID/reason code
   
    <drvnum>
   2 bytes. 16-bit floppy drive number.
   
   Data Returned:
   
    <status>
   2 byte error code: see error summary below.
   
    <NDrives>
   2  bytes.  Number  of floppy disk drives present.  Valid  drive
   numbers  are  in  the range 0..NDrives-1. If the  given  drvnum
   value  is not in this range, all the following parameters  will
   be zero (although no error is returned).
   
    <sizecode>
   1 byte. Disk drive size code:
    0 = no drive
    1 = 5.25" 360K (40 trk, 9 sectors, 2 heads)
    2 = 5.25" 1.2M (80 trk, 15 sectors, 2 heads)
    3 = 3.5" 720K (80 trk, 9 sectors, 2 heads)
    4 = 3.5" 1.44M (80 trk, 18 sectors, 2 heads)
    5..0FFh = to be defined
   
   Note: If the value is in the range 5-0FFh, the floppy drive  is
   not  one  of the standard sizes, although the following  fields
   will  still  give correct infornation. If possible, you  should
   write  a driver which ignores this byte and deal with any sized
   floppy which comes along.
   
    <flags>
   1 byte. Drive flags bit field:
     bit 0: 1 if drive can detect disk changes (i.e. HPC_FD_STATUS
   detects works reliably)
     bit 1: 1 if drive can operate with multiple density settings,
   etc (e.g. 720K in a 1.44M drive).
   
    all other bits reserved
   
    <Nheads>
   1 byte. Number of heads on drive, usually 1 or 2.
   
    <Nsects>
   1  byte. Number of (512-byte) sectors per track on drive at max
   recording density.
   
    <Ntracks>
   2 bytes. Number of tracks on drive (typically 40 or 80).
   
   
   B.3.2 HPC_FD_STATUS (0002h:0001h)
   Checks the drive-changed status of the drive.
   
   Data Sent:
   
    02h 00h 01h 00h
   Service ID/reason code
   
    <drvnum>
   2 bytes. 16-bit floppy drive number.
   
   Data Returned:
   
    <status>
   2  byte error code (see below): this will either be 0000h if no
   error,  0006h if the drive is not ready, or 0004h if  the  disk
   has been changed.
   
   
   B.3.3 HPC_FD_READ (0002h:0002h)
   Reads  one or more sectors from a floppy disk. If the drive  is
   capable  of  operating at more than one density setting  (etc),
   this  will automatically retry other settings if the first  one
   is unsuccessful.
   
   Data Sent:
   
    02h 00h 02h 00h
   Service ID/reason code
   
    <drvnum>
   2 bytes. 16-bit floppy drive number.
   
    <track>
   2 byte. Track number at which to start reading.
   
    <head>
   1 byte. Head number at which to start reading.
   
    <sect>
   1 byte. Sector number at which to start reading.
   
    <nsects>
   1 byte. Number of consecutive sectors to read.
   
    <sectsize>
   1  byte.  Log 2 of the size of the sector to be read.  Normally
   this value is 09h for a sector size of 512 bytes.
   
   
   Data Returned:
   
    <status>
   2 byte error code: see error summary below.
       
    <sects_read>
   1  byte.  Number  of  sectors successfully read.  If  an  error
   occurred in the middle of a read, this field may still be  non-
   zero to indicate some of the data is valid.
   
    <density>
   1  byte.  Code  indicating  settings  at  which  the  read  was
   successful.
   
   0 = read wasn't successful
   2 = single density (as in 180K disks)
   3 = single density with double-stepping
   4 = double density (as in 360K disks)
   5  =  double density with double-stepping (as in 360K disks  in
   1.2M drives)
   6 = quad ('high') density (as in 1.2M and 1.44M floppies)
   
   
    <read data>
   Data  bytes from disk. A total of sects_read << sectsize  bytes
   of this are valid.
   
   
   
   B.3.4 HPC_FD_WRITE (0002h:0003h)
   Writes data to a floppy disk.
   
   Data Sent:
   
    02h 00h 03h 00h
   Service ID/reason code
   
    <drvnum>
   2 bytes. 16-bit floppy drive number.
   
    <track>
   2 byte. Track number at which to start writing.
   
    <head>
   1 byte. Head number at which to start writing.
   
    <sect>
   1 byte. Sector number at which to start writing.
   
    <nsects>
   1 byte. Number of consecutive sectors to write.
   
    <sectsize>
   1 byte. Log 2 of the size of the sectors to be written.
   
    <write data>
   Data to be written, total length nsects << sectsize bytes.
   
   Data Returned:
   
    <status>
   2 byte error code: see error summary below.
   
    <sects_written>
   1 byte. Number of sectors successfully written.
   
    <density>
   1  byte.  Code  indicating density setting at which  write  was
   successful, as for HPC_FD_READ.
   
   
   B.3.5 HPC_FD_VERIFY (0002h:0004h)
   Verifies that sectors on a floppy disk contain valid data.
   
   Data Sent:
   
    02h 00h 04h 00h
   Service ID/reason code
   
    <drvnum>
   2 bytes. 16-bit floppy drive number.
   
    <track>
   2 bytes. Track number at which to start verify.
   
    <head>
   1 byte. Head number at which to start verify.
   
    <sect>
   1 byte. Sector number at which to start verify.
   
    <nsects>
   1 byte. Number of consecutive sectors to verify.
   
    <sectsize>
   1 byte. Log 2 of the size of the sectors.
   
   
   Data Returned:
   
    <status>
   2 byte error code: see error summary below.
   
    <sects_verified>
   1  byte.  Number of sectors successfully read. If a  sector  is
   defective, this will be the number of sectors successfully read
   before the error occurred.
   
    <density>
   1  byte.  Code  indicating density setting at  which  read  was
   successful (as for HPC_FD_READ).
   
   B.3.6 HPC_FD_FORMAT (0002h:0005h)
   Formats a track on a floppy disk.
   
   Data Sent:
   
    02h 00h 05h 00h
   Service ID/reason code
   
    <drvnum>
   2 bytes. 16-bit floppy drive number.
   
    <track>
   2 bytes. Track number for format.
   
    <head>
   1 byte. Head number for format.
   
    <nsects>
   1 byte. Number of sectors on this track to be formatted.
   
    <density>
   1  byte. Code for density setting during format. These are  the
   same as the values returned from
   HPC_FD_READ, HPC_FD_WRITE, etc, with the addition that 0 may be
   used for the 'default' format density (usually the highest).
   
    00h
   1 pad byte
   
    <format data>
   This  is  a table of nsects 4-byte entries. Each entry  in  the
   table is as follows:
    +00: Track number
    +01: Head number
    +02: Sector number
     +03:  Sector size (00=128 bytes, 01=256 bytes, 02=512  bytes,
   03=1024 bytes)
   
   
   Data Returned:
   
    <status>
   2 byte error code: see error summary below.
   
   
   B.3.7 Error codes
   0000h: OK
   0001h: Bad parameters to command
   0002h: Sector not found
   0003h: Disk is write protected
   0004h: Disk has been changed
   0005h: Data/CRC error
   0006h: Drive not ready
   0007h: Unknown reason code
   0008h: Disk format parameters not allowable
   FFFFh: Floppy services not available
   
   
B.4 CDROM services (service ID = 0003h)
   B.4.1 HPC_CD_INIT (0003h:0000h)
   Initialises the CDROM services, and checks drives
   
   Data Sent:
   
    03h 00h 00h 00h
   Service ID/reason code
   
   Data Returned:
   
    <status>
   2 byte error code (see below) - zero for success.
   
    <audio_status>
   2 bytes: always zero after initialisation
   
    <Ndrives>
   2 bytes. Number of CDROM drives available.
   
   
   
   B.4.2 HPC_CD_READ (0003h:0001h)
   Reads data from the CDROM disk.
   
   Data Sent:
   
    03h 00h 01h 00h
   Service ID/reason code
   
    <drvnum>
   2 bytes. CDROM drive number (in the range 0..Ndrives-1).
   
    <nsects>
   2  bytes. Number of (2Kbyte) sectors to read (1 to 7 with a 16K
   buffer).
   
    <sect_start>
   4  bytes.  Starting  sector number, as a 32-bit  logical  block
   address.
   
    <readahead>
   2 bytes. If this read is part of a larger transfer, this is the
   total  number of sectors (including this one) which are  to  be
   read.  Otherwise,  this field can be set to zero.  This  is  to
   allow  !PC  to  make  read-ahead  performance  enhancements  if
   possible.
   
   Data Returned:
   
    <status>
   2 byte error code (see below) - zero for success.
   
    <audio_status>
   2 byte flags field reflecting drive's current audio state
     bits 0..1:
      00 - Audio is not playing
      01 - Audio is currently playing
      10 - Audio is paused
   
    <read data>
   
   nsects*2048 bytes of data, if the read was successful.
   
   
   B.4.3 HPC_CD_SEEK (0003h:0002h)
   Used to seek the drive to a given position, in anticipation  of
   a read or play.
   
   Data Sent:
   
    03h 00h 02h 00h
   Service ID/reason code
   
    <drvnum>
   2 bytes. CDROM drive number (in the range 0..Ndrives-1).
   
    <nsects>
   2  bytes.  Expected  number of sectors to  be  read,  or  0  if
   unknown.
   
    <sect_seek>
   4  bytes.  Starting  sector number, as a 32-bit  logical  block
   address.
   
   Data Returned:
   
    <status>
   2 byte error code (see below) - zero for success.
   
    <audio_status>
   2  byte flags field reflecting drive's current audio state,  as
   above.
   
   
   B.4.4 HPC_CD_PLAY (0003h:0003h)
   Begins playing audio from the CDROM drive.
   
   Data Sent:
   
    03h 00h 03h 00h
   Service ID/reason code
   
    <drvnum>
   2 bytes. CDROM drive number (in the range 0..Ndrives-1).
   
    00h 00h
   Pad bytes.
   
    <start>
   4  bytes.  32-bit  logical block address where  playing  is  to
   start.
   
    <length>
   4 bytes. Count of frames (1/75 sec) of audio to be played.
   
   Data Returned:
   
    <status>
   2 byte error code (see below) - zero for success.
   
    <audio_status>
   2  byte flags field reflecting drive's current audio state,  as
   above.
   
   
   B.4.5 HPC_CD_PLAYSTATUS (0003h:0004h)
   Returns info about currently-playing audio on a CDROM drive.
   
   Data Sent:
   
    03h 00h 04h 00h
   Service ID/reason code
   
    <drvnum>
   2 bytes. CDROM drive number (in the range 0..Ndrives-1).
   
   Data Returned:
   
    <status>
   2 byte error code (see below) - zero for success.
   
    <audio_status>
   2  byte flags field reflecting drive's current audio state,  as
   for HPC_CD_PLAY
   
    <play_start>
   4 bytes. 32-bit logical block address where playing started.
   
    <play_current>
   4 bytes. 32-bit logical block address of current play position.
   
    <play_stop>
   4  bytes.  32-bit  logical  block address  where  playing  will
   finish.
   
   
   B.4.6 HPC_CD_DISKINFO (0003h:0005h)
   Returns information on the current CD in the drive.
   
   Data Sent:
   
    03h 00h 05h 00h
   Service ID/reason code
   
    <drvnum>
   2 bytes. CDROM drive number (in the range 0..Ndrives-1).
   
   Data Returned:
   
    <status>
   2 byte error code (see below) - zero for success.
   
    <audio_status>
   2  byte flags field reflecting drive's current audio state,  as
   above.
   
    <disksize>
   4 bytes. Size of the disk in blocks, or 0 if drive is empty.
   
   
   B.4.7 HPC_CD_TRACKINFO (0003h:0006h)
   Returns information about audio tracks on the current disk.
       
   Data Sent:
   
    03h 00h 06h 00h
   Service ID/reason code
   
    <drvnum>
   2 bytes. CDROM drive number (in the range 0..Ndrives-1).
   
    <trknum>
   2 bytes. Track number for which information is requested, or -1
   for whole CD info.
   
   Data Returned:
   
    <status>
   2 byte error code (see below) - zero for success.
   
    <audio_status>
   2  byte flags field reflecting drive's current audio state,  as
   above.
   
    <start>
   4  bytes.  Starting position (as a 32-bit LBA) of  audio  track
   given by trknum, or size of disk if trknum is -1.
   
    <first>
   1 byte. Track number of first audio track (if trknum is -1)
   
    <last>
   1 byte. Track number of last audio track (if trknum is -1)
   
    <flags>
   1 byte. Flags for audio track given by trknum.
   bit 6: 1 if track contains data
   bit 7: 1 if track contains 4-channel sound
   
   
   B.4.8 HPC_CD_QCHANNEL (0003h:0007h)
   Gets data from the Q subchannel for currently playing audio.
   
   Data Sent:
   
    03h 00h 07h 00h
   Service ID/reason code
   
    <drvnum>
   2 bytes. CDROM drive number (in the range 0..Ndrives-1).
   
   Data Returned:
   
    <status>
   2 byte error code (see below) - zero for success.
   
    <audio_status>
   2  byte flags field reflecting drive's current audio state,  as
   above.
   
    <Q_data>
   10 bytes, data from Q channel of currently playing track:
   
   +00: Control and address byte (normally 1)
   +01: Track number
   +02: Point or Index
   +03,+04,+05 Minute,second,frame of running time within track
   +06: 0
   +07,+08,+9 Minute,second,frame of running time on disk
   
   
   
   B.4.9 HPC_CD_CONTROL (0003h:0008h)
   Performs miscellaneous control functions on a CDROM drive.
   
   Data Sent:
   
    03h 00h 08h 00h
   Service ID/reason code
   
    <drvnum>
   2 bytes. CDROM drive number (in the range 0..Ndrives-1).
   
    <fncode>
   Subfunction code, as follows:
   
   0000h - Reset CDROM drive (stop audio, etc)
   0001h - Disk changed check (returns code 0004h if disk has been
   changed & clears
                  disk-changed status)
   0002h - Pause playing audio at current position
   0003h - Resume playing audio at current position
   0004h - Enable eject button
   0005h - Disable eject button
   0006h - Open CD-ROM drawer
   0007h - Close CD-ROM drawer
   
   Data Returned:
   
    <status>
   2 byte error code (see below) - zero for success.
   
    <audio_status>
   2  byte flags field reflecting drive's current audio state,  as
   for HPC_CD_PLAY
   
   
   B.4.10 Status codes
   The following values may be returned:
   
   0          Success
   0001h      Bad reason code
   0002h      Bad drive number
   0003h      Bad parameters
   0004h      CD has been changed
   0005h      Audio is playing; cannot complete command
                (returned from Seek, Read, Play, etc)
   0006h      Read data error
   0007h      Error from CDROM drive
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
